﻿using System;
using Microsoft.SPOT;
using System.Net.Sockets;
using Microsoft.SPOT.Hardware;
using System.Net;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using System.Text;
using System.Threading;
using Bt.Mvc;
using Bt.WebServer;
using Bt.Core;

namespace Bt.DoorWebServer
{
    public class WebServer : IDisposable
    {
        private Controller controller;

        private Socket socket = null;
        //open connection to onbaord led so we can blink it with every request
        private OutputPort led = new OutputPort(Pins.ONBOARD_LED, false);

        public WebServer(Controller controller)
        {
            this.controller = controller;
            
            //Initialize Socket class
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
            //Request and bind to an IP from DHCP server
            socket.Bind(new IPEndPoint(IPAddress.Any, 80));
            
            //Debug print our IP address
            var clientIpAddress = Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].IPAddress;
            string url = "http://" + clientIpAddress + "/";
            Debug.Print(url);
            Log.Write(url);

            //Start listen for web requests
            socket.Listen(10);
            ListenForRequest();
        }


        private int clientCount = 0;
        public void ListenForRequest()
        {
            while (true)
            {
                var memAvailable = Microsoft.SPOT.Debug.GC(false);
                Debug.Print("memAvailable: " + memAvailable.ToString());
                Debug.Print("clientCount: " + this.clientCount.ToString());
                using (Socket clientSocket = socket.Accept())
                {
                    this.clientCount++;
                    //Get clients IP
                    IPEndPoint clientIP = clientSocket.RemoteEndPoint as IPEndPoint;
                    Log.Write("Client " + clientIP.Address.ToString() + " connected\r\n");
                    

                    // EndPoint clientEndPoint = clientSocket.RemoteEndPoint;                    
                    //int byteCount = cSocket.Available;
                    int bytesReceived = clientSocket.Available;
                    if (bytesReceived > 0)
                    {
                        //Get request
                        byte[] buffer = new byte[bytesReceived];
                        int byteCount = clientSocket.Receive(buffer, bytesReceived, SocketFlags.None);
                        string request = new string(Encoding.UTF8.GetChars(buffer));
                        Debug.Print(request);
                        Log.Write(request);

                        //Compose a response

                        var viewResult = this.controller.ExecuteRequest(new HttpRequest(request));

                        // Render Body 
                        string response=""; 
                        try
                        {
                            response = viewResult.RenderBody();
                        }
                        catch(Exception ex)
                        {
                            Log.Write(ex.ToString());
                        }
                        // response += Log.Read();
                        
                        var responseBytes = Encoding.UTF8.GetBytes(response);

                        string header = "HTTP/1.0 " + viewResult.StatusCode + "\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " + responseBytes.Length.ToString() + "\r\nConnection: close\r\n\r\n";
                        
                        var headerBytes = Encoding.UTF8.GetBytes(header);
                        try
                        {
                            clientSocket.Send(headerBytes, headerBytes.Length, SocketFlags.None);
                            clientSocket.Send(responseBytes, responseBytes.Length, SocketFlags.None);
                        }
                        catch(Exception ex)
                        {
                            Debug.Print(ex.Message);
                        }
                        finally
                        {
                            clientSocket.Close();
                            this.clientCount--;
                        }

                        //Blink the onboard LED
                        led.Write(true);
                        Thread.Sleep(150);
                        led.Write(false);
   
                    }
                    else
                    {
                        clientSocket.Close();
                        this.clientCount--;
                    }
                }
            }
        }
        #region IDisposable Members
        ~WebServer()
        {
            Dispose();
        }
        public void Dispose()
        {
            if (socket != null)
                socket.Close();
        }
        #endregion
    }
}
